import * as socket from 'ws';
import system from "./system";
import eventEmit from './emitEvent';
import {command} from './AtCommand';
import { logger } from './log';
import DeviceStatusManager from './DeviceStatusManager';
import TcpClient from './Tcp';
import { GatewayIncludesDevice } from './DeviceStatusManager';


// 使用单列模式
class websocketIo {
    public static instance; // 实列对象
    public socketObj:any = null; // pc端通信对象
    static isConfig: boolean = false;
    private deviceStatusManager: DeviceStatusManager;
    private tcpClient: TcpClient;
    private rejectedDevices: Map<string, Set<string>> = new Map(); // 存储被拒绝的设备ESN，key为网关IP，value为该网关下被拒绝的设备ESN集合
    private pendingResponses: Map<string, {client: any, commandType: string, timeout: NodeJS.Timeout}> = new Map(); // 存储待响应的指令
    constructor() {
        this.deviceStatusManager = DeviceStatusManager.getInstance();
        this.tcpClient = TcpClient.getInstance();
        
        // 设置AT指令响应回调
        this.tcpClient.setATResponseCallback((address: string, response: string) => {
            this.handleATResponse(address, response);
        });
        
        // 监听设备掉线事件
        this.deviceStatusManager.onDeviceOffline((event) => {
            this.notifyDeviceOffline(event);
        });
        
        // 启动待响应指令清理定时器（每30秒清理一次）
        setInterval(() => {
            this.cleanupExpiredPendingResponses();
        }, 30000);
    }
    
    static get getInstance() {
        if (!this.instance) {
            this.instance = new websocketIo()
        }
        return this.instance
    }

    // 通知前端网关的数量
    notifyGatewayModuleNum(gatewayIp: string) {
        // 查询当前网关下面的数量
        const deviceSet:Set<GatewayIncludesDevice> = this.deviceStatusManager.getGatewayIncludesDevice(gatewayIp)
        
        // 查询所有网关下面的设备数量
        const totalDevices:Set<GatewayIncludesDevice> = this.deviceStatusManager.getTotalDevices()

        if (this.socketObj) {
            // 通知前端当前网关下面的设备数量
            this.socketObj.send(JSON.stringify({
                gateway_ip: gatewayIp,
                action: 'getModuleNum',
                size: deviceSet.size,
                deviceList: Array.from(totalDevices)
            }))

        }
    }

    // 通知前端待人网的设备
    notifyPendingDevices() {
        let allDevices: any[] = []
        this.tcpClient.sockets.forEach((socket) => {
            if (socket.isConnected) {
                let address = socket.address // 网关的ip地址
                let rejectedDevices = this.getRejectedDevices(address)
                allDevices.push(...rejectedDevices)
            }
        })
       if (this.socketObj) {
        // 发送前端待人网的设备有哪些
        this.socketObj.send(JSON.stringify({
            action: 'pendingDevices',
            deviceList: allDevices
        }))
       }
    }
    // 连接对象
    connection(port:number) {
         let socketObj = new socket.Server({port})
         socketObj.on("connection",(client:any,upgradeReq) => {
            this.socketObj = client
            logger.info("设备连接成功")
            client.on("close", () => {
                logger.info("断开连接")
                this.socketObj = null
            })
             // 接收数据
             client.on("message", async (msg: any) => {
                let data: Record<string, any>;
                try {
                    if (typeof msg === 'string') {
                        data = JSON.parse(msg);
                    } else if (msg instanceof Buffer || Buffer.isBuffer(msg)) {
                        data = JSON.parse(msg.toString());
                    } else if (typeof msg === 'object') {
                        data = msg; // 已经是对象
                    } else {
                        throw new Error('Unknown message type');
                    }
                } catch (e) {
                    logger.error('消息解析失败:', e, msg);
                    return;
                }
                if (data.action === "taggleGateway") {
                    // 切换网关
                    this.tcpClient.currentGatewayIp = data.gatewayIp;

                    console.log("切换网关" + data.gatewayIp)

                    // 通知网关在线数量
                    this.notifyGatewayModuleNum(data.gatewayIp)

                    // 通知前端有那些待入网的设备
                    this.notifyPendingDevices()

                }  else if (data.action === 'changeVal') {
                    // 改变阈值
                    let errorVal: string | number = data.errorVal //报警值
                    let module_id: number = data.module_id // 模块编号
                    let type = data.type_id // 阈值类型 1~5 紫外、红外、温度、湿度、压力
                    // 改变服务端的阈值
                    eventEmit.changeVal(module_id,type,errorVal)
                } else if (data.action === 'get_val') {
                    // 获取阈值
                    client.send(JSON.stringify({
                        action: 'get_val',
                        data: eventEmit.settingData
                    }))
                } else if (data.action === 'getDeviceStats') {
                    // 获取设备统计信息
                    const stats = this.deviceStatusManager.getDeviceStatistics();
                    client.send(JSON.stringify({
                        action: 'deviceStats',
                        data: stats
                    }));
                } else if (data.action === 'gateway') {
                    let gatewayType: string = data.type
                    let gatewayIp: string = data.gatewayId

                    switch(gatewayType) {
                        case 'toggleMode': {
                            eventEmit.type = data.mode
                        };break;    
                        case 'add': {
                            // 添加网关
                            logger.info(`向网关 ${gatewayIp} 发送添加指令`)
                            let atstring: string = command.addTerminal(data.esn,data.powerType,data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'add')
                            let device: GatewayIncludesDevice = {
                                esn: data.esn,
                                deviceId: data.id,
                                status: true,
                                gatewayIp
                            }
                            // 添加到列表当中
                            this.deviceStatusManager.addGatewayIncludesDevice(gatewayIp, device)
                                             
                            // 通知网关在线数量
                            this.notifyGatewayModuleNum(data.gatewayIp)

                            // 通知前端有那些待入网的设备
                            this.notifyPendingDevices()
                        };break;
                        case 'update': {
                            // 更改设备类型
                            logger.info(`向网关 ${gatewayIp} 发送更改设备类型指令`)
                            let atstring: string = command.updateTerminal(data.esn,data.powerType,data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'update')
                        };break;
                        case 'delete': {
                            // 删除网关
                            logger.info(`向网关 ${gatewayIp} 发送删除指令`)
                            let atstring: string = command.deleteTerminal(data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'delete')

                            // 删除后清除掉拒绝列表中该设备
                            this.deviceStatusManager.deleteGatewayIncludesDevice(gatewayIp, data.id)

                            // 删除设备状态（通过时间来判断是否删除）
                            this.deviceStatusManager.deleteGatewayDeviceData(gatewayIp, data.id)

                            // 通知前端状态更新
                            this.notifyDeviceStatusUpdate();
                        };break;
                        case 'queryAll': {
                            logger.info(`向网关 ${gatewayIp} 发送查询所有指令`)
                            let atstring: string = command.queryTerminalInfo()
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'queryAll')
                        };break;
                        case 'queryDeviceType': {
                            logger.info(`向网关 ${gatewayIp} 发送查询设备类型指令`)
                            let atstring: string = command.queryDeviceType(data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'queryDeviceType')
                        };break;
                        case "queryVersion": {
                            logger.info(`向网关 ${gatewayIp} 发送查询版本指令`)
                            let atstring: string = command.queryVersion(data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'queryVersion')
                        };break;
                        case "queryCircuitGain": {
                            logger.info(`向网关 ${gatewayIp} 发送查询电路增益指令`)
                            let atstring: string = command.queryCircuitGain(data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'queryCircuitGain')
                        };break;
                        case "setCircuitGain": {
                            logger.info(`向网关 ${gatewayIp} 发送设置电路增益指令`)
                            let atstring: string = command.setCircuitGain(data.id,data.gain)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'setCircuitGain')
                        };break;
                        case "queryGateway": {
                            logger.info(`向网关 ${gatewayIp} 发送查询网关ID指令`)
                            let atstring: string = command.queryGatewayId()
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'queryGateway')
                        };break;
                        case "device_reboot": {
                            logger.info(`向网关 ${gatewayIp} 发送设备重启指令`)
                            let atstring: string = command.reboot(data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'device_reboot')
                        };break;
                        case "setFilterRatio": {
                            logger.info(`向网关 ${gatewayIp} 发送设置滤波器系数指令`)
                            let atstring: string = command.setFilterCoefficient(data.id, data.ratio)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'setFilterRatio')
                        };break;
                        case "queryFilterRatio": {
                            logger.info(`向网关 ${gatewayIp} 发送查询滤波器系数指令`)
                            let atstring: string = command.queryFilterCoefficient(data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'queryFilterRatio')
                        };break;
                        case "device_root":{
                            logger.info(`向网关 ${gatewayIp} 发送设备重启指令`)
                            let atstring: string = command.reboot(data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'device_reboot')
                        };break;
                        case "queryUvThreshold": {
                            logger.info(`向网关 ${gatewayIp} 发送查询紫外阈值指令`)
                            let atstring: string = command.queryUvThreshold(data.id)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'queryUvThreshold')
                        };break;
                        case "setUvThreshold":{
                            logger.info(`向网关 ${gatewayIp} 发送设置紫外阈值指令`)
                            let atstring: string = command.setUvThreshold(data.id,data.threshold)
                            this.sendCommandToGateway(gatewayIp, atstring, client, 'setUvThreshold')
                        }break;
                        case "queryPendingDevices": {
                            logger.info(`前端请求查询网关 ${gatewayIp} 的待加入子设备ESN列表`);
                            this.notifyPendingDevices()
                        };break;
                    }
                }
             })
        })
    }
    
    /**
     * 通知设备掉线事件
     */
    private notifyDeviceOffline(event: any): void {
        if (this.socketObj) {
            this.socketObj.send(JSON.stringify({
                action: 'deviceOffline',
                data: event
            }));

            // 通知前端网关的设备数量更新
            this.notifyGatewayModuleNum(event.gatewayIp)

            // 通知待入网子设备列表更新
            this.notifyPendingDevices()
            
            logger.info(`通知前端设备 ${event.deviceId} 掉线`);
        }
    }
    
    /**
     * 通知设备状态更新
     */
    public notifyDeviceStatusUpdate(): void {
        if (this.socketObj) {
            const stats = this.deviceStatusManager.getDeviceStatistics();
            this.socketObj.send(JSON.stringify({
                action: 'deviceStatsUpdate',
                data: stats
            }));
        }
    }
    /**
     * 记录设备数据接收（供外部调用）
     */
    public recordDeviceData(deviceId: string, gatewayIp: string,data?: { battery?: number, signal?: number }): void {
        this.deviceStatusManager.recordDeviceData(deviceId, gatewayIp,data);
        // 通知前端状态更新
        this.notifyDeviceStatusUpdate();
    }
    // 记录设备的状态和ESN码
    private recordDeviceInfo(address: string,info: string) {
        if (info.startsWith("FF")) {
            // 这是一个设备ESN的查询
            let deviceList: string[] = info.split(",")
            if (deviceList.length !== 3) return logger.error(`[websocket.io.recordDeviceInfo] 设备ESN查询格式错误: ${info}`)
            let device: GatewayIncludesDevice = {
                esn: deviceList[0],
                deviceId: deviceList[1],
                status: Boolean(Number(deviceList[2])),
                gatewayIp: address
            }
            // 添加设备
            this.deviceStatusManager.addGatewayIncludesDevice(address,device)

            // 添加成功之后需要通知前端有那些待入网的设备
            this.notifyPendingDevices()
            // 通知前端当前设备的列表
            this.notifyGatewayModuleNum(address)
            // 通知前端状态更新
            this.notifyDeviceStatusUpdate();

            logger.info(`[websocket.io.recordDeviceInfo] 记录设备信息:`, device)
        }
    }
    /**
     * 处理AT指令响应
     */
    private handleATResponse(address: string, response: string): void {
        // logger.info(`[websocket.io.handleATResponse]处理来自网关 ${address} 的AT指令响应:`, response);


        // 检查是否是网关的设备查询
        this.recordDeviceInfo(address,response)
        
        // 检查是否为 AT+DEV_REJECT 指令
        if (response.includes("AT+DEV_REJECT")) {
            // 提取设备ESN（假设格式为 AT+DEV_REJECT: FF0100022D0C）
            const esnMatch = response.match(/AT\+DEV_REJECT:\s*([A-F0-9]+)/);
            if (esnMatch && esnMatch[1]) {
                const deviceESN = esnMatch[1];
                if (!this.rejectedDevices.has(address)) {
                    this.rejectedDevices.set(address, new Set<string>());
                }
                this.rejectedDevices.get(address)?.add(deviceESN);
            }
            // console.log('收到网关拒绝加入子设备指令:', esnMatch);
            // AT+DEV_REJECT 指令不返回给前端，避免干扰其他指令
            return;
        }
        
        // 判断响应结果
        const isSuccess = response.includes("OK") && !response.includes("ERROR");
        const isError = response.includes("ERROR");
        
        // 查找并处理对应的待响应指令
        let responseHandled = false;
        this.pendingResponses.forEach((pendingResponse, key) => {
            if (key.startsWith(address) && !responseHandled) {
                // 清理超时定时器
                clearTimeout(pendingResponse.timeout);
                
                // 向对应的客户端发送响应
                pendingResponse.client.send(JSON.stringify({
                    action: "gatewayCommandResponse",
                    commandType: pendingResponse.commandType,
                    gatewayIp: address,
                    success: isSuccess,
                    error: isError ? response : null,
                    response: response,
                    message: isSuccess ? '指令执行成功' : (isError ? '指令执行失败' : '收到响应'),
                    timestamp: new Date().toISOString()
                }));
                
                // 移除已处理的待响应记录
                this.pendingResponses.delete(key);
                responseHandled = true;
            }
        });
        
        // 如果没有找到对应的待响应指令，使用广播方式（兼容旧逻辑）
        if (!responseHandled && this.socketObj) {
            this.socketObj.send(JSON.stringify({
                action: "gatewayCommandResponse",
                gatewayIp: address,
                success: isSuccess,
                error: isError ? response : null,
                response: response,
                message: isSuccess ? '指令执行成功' : (isError ? '指令执行失败' : '收到响应'),
                timestamp: new Date().toISOString()
            }));
        }
    }
    
    /**
     * 获取被拒绝的设备列表
     */
    public getRejectedDevices(gatewayIp?: string): { gatewayIp: string, esns: string[] }[] | string[] {
        if (gatewayIp) {
            return Array.from(this.rejectedDevices.get(gatewayIp) || []);
        } else {
            const allRejected: { gatewayIp: string, esns: string[] }[] = [];
            this.rejectedDevices.forEach((esns, ip) => {
                allRejected.push({ gatewayIp: ip, esns: Array.from(esns) });
            });
            return allRejected;
        }
    }
    
    /**
     * 清空被拒绝的设备列表
     */
    public clearRejectedDevices(gatewayIp?: string): void {
        if (gatewayIp) {
            this.rejectedDevices.delete(gatewayIp);
        } else {
            this.rejectedDevices.clear();
        }
    }
    
    /**
     * 检查设备是否在拒绝列表中
     */
    public isDeviceRejected(gatewayIp: string, deviceESN: string): boolean {
        return this.rejectedDevices.has(gatewayIp) && (this.rejectedDevices.get(gatewayIp)?.has(deviceESN) || false);
    }
    
    /**
     * 向指定网关发送指令
     * @param gatewayIp 网关IP地址
     * @param command 要发送的AT指令
     * @param client WebSocket客户端对象
     * @param commandType 指令类型
     */
    private sendCommandToGateway(gatewayIp: string, command: string, client: any, commandType: string): void {
        try {
            // 查找对应的TCP连接
            const connection = this.tcpClient.sockets.get(gatewayIp);
            if (!connection || !connection.isConnected) {
                // 向前端发送错误响应
                client.send(JSON.stringify({
                    action: 'gatewayCommandResponse',
                    commandType: commandType,
                    gatewayIp: gatewayIp,
                    success: false,
                    error: `网关 ${gatewayIp} 未连接或连接不可用`,
                    timestamp: new Date().toISOString()
                }));
                return;
            }
            
            // 生成唯一的响应键
            const responseKey = `${gatewayIp}_${commandType}_${Date.now()}`;
            
            // 设置超时处理（15秒超时）
            const timeout = setTimeout(() => {
                logger.warn(`指令 ${commandType} 向网关 ${gatewayIp} 发送超时`);
                // 清理待响应记录
                this.pendingResponses.delete(responseKey);
                // 向前端发送超时响应
                client.send(JSON.stringify({
                    action: 'gatewayCommandResponse',
                    commandType: commandType,
                    gatewayIp: gatewayIp,
                    success: false,
                    error: '指令执行超时，网关未在规定时间内响应',
                    timeout: true,
                    timestamp: new Date().toISOString()
                }));
            }, 15000); // 15秒超时
            
            // 记录待响应的指令
            this.pendingResponses.set(responseKey, {
                client: client,
                commandType: commandType,
                timeout: timeout
            });
            
            // 使用新的sendATCommand方法发送指令
            const success = this.tcpClient.sendATCommand(gatewayIp, command, commandType);
            if (success) {
                logger.debug(`AT指令已发送到网关 ${gatewayIp}: ${command}`);
                // 发送指令成功的初始响应（实际结果将通过AT指令响应返回）
                client.send(JSON.stringify({
                    action: 'gatewayCommandSent',
                    commandType: commandType,
                    gatewayIp: gatewayIp,
                    success: true,
                    command: command,
                    message: 'AT指令发送成功，等待网关响应...',
                    responseKey: responseKey,
                    timestamp: new Date().toISOString()
                }));
            } else {
                // 发送失败，清理超时和待响应记录
                clearTimeout(timeout);
                this.pendingResponses.delete(responseKey);
                
                logger.error(`向网关 ${gatewayIp} 发送AT指令失败: ${command}`);
                // 向前端发送失败响应
                client.send(JSON.stringify({
                    action: 'gatewayCommandResponse',
                    commandType: commandType,
                    gatewayIp: gatewayIp,
                    success: false,
                    error: 'AT指令发送失败',
                    timestamp: new Date().toISOString()
                }));
            }
        } catch (error) {
            logger.error(`发送AT指令到网关 ${gatewayIp} 时发生异常:`, error);
            // 向前端发送异常响应
            client.send(JSON.stringify({
                action: 'gatewayCommandResponse',
                commandType: commandType,
                gatewayIp: gatewayIp,
                success: false,
                error: `发送AT指令时发生异常`,
                timestamp: new Date().toISOString()
            }));
        }
     }
     
     /**
      * 清理过期的待响应指令
      */
     private cleanupExpiredPendingResponses(): void {
         const now = Date.now();
         const expiredKeys: string[] = [];
         
         this.pendingResponses.forEach((pendingResponse, key) => {
             // 从key中提取时间戳
             const keyParts = key.split('_');
             const timestamp = parseInt(keyParts[keyParts.length - 1]);
             
             // 如果超过20秒未响应，认为已过期
             if (now - timestamp > 20000) {
                 expiredKeys.push(key);
                 // 清理超时定时器
                 clearTimeout(pendingResponse.timeout);
             }
         });
         
         // 移除过期的记录
         expiredKeys.forEach(key => {
             this.pendingResponses.delete(key);
         });
         
         if (expiredKeys.length > 0) {
             logger.debug(`已清理 ${expiredKeys.length} 个过期的待响应指令`);
         }
     }
}

export default websocketIo
